package rsc

import (
	"distributed_db/utils"
	"distributed_db/utils/db"
	"net"
	"net/http"
	"net/rpc"
	"strings"
)

/* TODO：关于架构的问题
事务是在master存储的，不可能在regionserver中使用或获取。
因此，master中的一些变量应该下放到RegionServerConnection中
*/

type RegionServerConnection struct {
	ID       string
	IP       string
	Port     string
	Username string
	Password string
	DbName   string
	Regions  []db.Region
	conn     db.DBConnection
	listener net.Listener
}

// NewRSC 新建一个RSC实例
func NewRSC(id string, ip string, port string, username string, password string, dbName string) *RegionServerConnection {
	rsc := &RegionServerConnection{
		ID:       id,
		IP:       ip,
		Port:     port,
		Username: username,
		Password: password,
		DbName:   dbName,
	}
	rsc.Regions = make([]db.Region, 0)
	rsc.conn = *db.NewDBConnection(ip, "3306", username, password, dbName)

	// gob.Register(sql.Tx{}) 但是注册还是传不了sql.Tx这个参数

	// 注册rpc服务
	go rsc.SyncRegisterRPC()
	return rsc
}

func (rsc *RegionServerConnection) SyncRegisterRPC() {
	// 注册rpc服务
	s := rpc.NewServer()
	s.Register(rsc)

	listener, err := net.Listen("tcp", rsc.IP+":"+rsc.Port)
	if err != nil {
		utils.LogC(utils.Red, "Listen error: %v", err)
	}
	rsc.listener = listener

	http.Serve(listener, s)
}

func (rsc *RegionServerConnection) InsertRegion(args *utils.InsertRegionArgs, reply *utils.InsertRegionReply) error {
	// 调用db.InsertRegion()方法

	ans, err := rsc.conn.InsertRegion(nil, args.Table, args.Columns, args.PrimaryKeys, args.Indexes, args.Values)
	if err != nil {
		reply.Err = err
		return err
	}
	reply.Ans = ans
	return nil
}

func (rsc *RegionServerConnection) QueryRegion(args *utils.QueryRegionArgs, reply *utils.QueryRegionReply) error {
	// 调用db.QueryRegion()方法
	regions, err := rsc.conn.GetAllRegions()
	if err != nil {
		reply.Err = err
		return err
	}
	res_data := make([]map[string]interface{}, 0)
	for _, region := range regions {
		if strings.Compare(strings.Split(region.RegionName, "_")[0], args.Table) == 0 {
			data, err := rsc.conn.QueryRegion(nil, region.RegionName, args.Row, args.Cond)
			if err != nil {
				reply.Err = err
				return err
			}
			res_data = append(res_data, data...)
		}
	}
	reply.Data = res_data
	return nil
}

func (rsc *RegionServerConnection) DeleteRegion(args *utils.DeleteRegionArgs, reply *utils.DeleteRegionReply) error {
	// 调用db.DeleteRegion()方法
	ans, err := rsc.conn.DeleteRegion(nil, args.Table, args.Cond)
	if err != nil {
		reply.Err = err
		return err
	}
	reply.Ans = ans
	return nil
}

func (rsc *RegionServerConnection) UpdateRegion(args *utils.UpdateRegionArgs, reply *utils.UpdateRegionReply) error {
	// 调用db.UpdateRegion()方法
	err := rsc.conn.UpdateRegion(nil, args.Table, args.Clause, args.Cond)
	if err != nil {
		reply.Err = err
		return err
	}
	return nil
}

func (rsc *RegionServerConnection) GetRegionData(args *utils.GetRegionDataArgs, reply *utils.GetRegionDataReply) error {
	// 调用db.GetAllRegions()方法
	cols, data, err := rsc.conn.GetRegionData(args.Table)
	if err != nil {
		reply.Err = err
		return err
	}
	reply.Cols = cols
	reply.Data = data
	return nil
}

func (rsc *RegionServerConnection) GetAllRegions(args *utils.GetAllRegionsArgs, reply *utils.GetAllRegionsReply) error {
	// 调用db.GetAllRegions()方法
	regions, err := rsc.conn.GetAllRegions()
	if err != nil {
		reply.Err = err
		return err
	}
	reply.Regions = regions
	return nil
}

func (rsc *RegionServerConnection) DropRegion(args *utils.DropRegionArgs, reply *utils.DropRegionReply) error {
	// 调用db.DropRegion()方法
	err := rsc.conn.DropRegion(args.Region)
	if err != nil {
		reply.Err = err
		return err
	}
	return nil
}

func (rsc *RegionServerConnection) DropAllRegions(args *utils.DropAllRegionsArgs, reply *utils.DropAllRegionsReply) error {
	err := rsc.conn.DropAllRegions()
	if err != nil {
		reply.Err = err
		return err
	}
	return nil
}

func (rsc *RegionServerConnection) CreateIndex(args *utils.CreateIndexArgs, reply *utils.CreateIndexReply) error {
	// 调用db.CreateIndex()方法
	err := rsc.conn.CreateIndex(args.Table, args.Indexes)
	if err != nil {
		reply.Err = err
		return err
	}
	return nil
}

// Close 关闭RSC实例
func (rsc *RegionServerConnection) Close() {
	// 关闭rpc服务
	// 释放db连接
	rsc.conn.ReleaseConnection()
	rsc.listener.Close()
}
